﻿using Bit.Art.Api.Controllers;
using Bit.Art.Core.Extensions;
using Bit.Art.Db;
using Bit.Art.Distributed;
using Bit.Art.Engine;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.Linq;
using System.Threading.Tasks;

namespace bit.art.test
{
#if UNITEST
    [TestClass]
    public class 集成异常测试
    {
        protected static Memcache s_Memcache;

        [ClassInitialize]
        public static void 启动消息队列各个节点(TestContext context)
        {
            ApplicationLogging.LoggerFactory.AddConsole().AddDebug(LogLevel.Warning);
            s_Memcache = Memcache.Create("localhost");
            Bit.Art.Broker.Program.StartEngine();
            Bit.Art.Account.Program.StartEngine(s_Memcache);
            Bit.Art.Bookeeper.Program.StartEngine(s_Memcache);
            Bit.Art.Engine.Program.StartEngine(s_Memcache, 5, 100);
            Bit.Art.MySqlDb.Program.StartEngine();
            // 等待1秒，确保所有节点都启动了
            Bit.Art.Broker.Program.s_autoResetEventForTest.WaitOne(1000);
            Bit.Art.Account.Program.s_autoResetEventForTest.WaitOne(1000);
            Bit.Art.Bookeeper.Program.s_autoResetEventForTest.WaitOne(1000);
            Bit.Art.Engine.Program.s_autoResetEventForTest.WaitOne(1000);
            Bit.Art.MySqlDb.Program.s_autoResetEventForTest.WaitOne(1000);
        }

        [TestCleanup]
        public void TestCleanup()
        {
            Bit.Art.Account.Program.s_autoResetEventForTest.Reset();
            Bit.Art.Bookeeper.Program.s_autoResetEventForTest.Reset();
            Bit.Art.Bookeeper.Program.s_filledAutoEventForTest.Reset();
            Bit.Art.Engine.Program.s_autoResetEventForTest.Reset();
            Bit.Art.MySqlDb.Program.s_autoResetEventForTest.Reset();

            using (var db = new ExchangeDb())
            {
                db.Database.ExecuteSqlCommand("DELETE FROM exchangedb.orders WHERE Id <> @p0; " +
                    "DELETE FROM exchangedb.accountbalances WHERE Id <> @p0", Guid.Empty);
            }
        }

        [TestMethod]
        public async Task 集成测试单一价格超出容量的订单()
        {
            var controller = new MarketController();
            controller.SetMemcache(s_Memcache);
            var market = "BTC/USDT";
            decimal bidprice = 1000, askprice = 1001;
            var orderCapacity = 100;

            for (var i = 0; i < orderCapacity / 2; ++i)
            {
                controller._uid = TestConstants.DEFAULT_BID_USER_ID;
                await controller.BuyLimit(market, bidprice, 1);
                controller._uid = TestConstants.DEFAULT_ASK_USER_ID;
                await controller.SellLimit(market, askprice, 1);
            }

            // 延时5秒
            await Task.Delay(5000);
            Assert.AreEqual(orderCapacity, OrderMatchHandler.s_MemOrderBookRepo[0].Capacity);
            Assert.AreEqual(orderCapacity, OrderMatchHandler.s_MemOrderBookRepo[0].Count);

            // 再下一个买单
            controller._uid = TestConstants.DEFAULT_BID_USER_ID;
            await controller.BuyLimit(market, bidprice, 1);
            await Task.Delay(1000);
            Assert.AreEqual(orderCapacity, OrderMatchHandler.s_MemOrderBookRepo[0].Capacity);
            Assert.AreEqual(orderCapacity, OrderMatchHandler.s_MemOrderBookRepo[0].Count);

            // 数据库里应该有这笔订单！
            using (var db = new ExchangeDb())
            {
                var count = db.Orders.Count();
                Assert.AreEqual(orderCapacity + 1, count);
                var matched = db.Orders.Count(o => o.VolumeRemaining != o.Volume);
                Assert.AreEqual(0, matched);
            }

            // 下一个超级卖单
            controller._uid = TestConstants.DEFAULT_ASK_USER_ID;
            await controller.SellLimit(market, bidprice - 1, 1 * orderCapacity / 2);
            for (var i = 0; i < orderCapacity / 2 + 1; ++i)
                Bit.Art.Bookeeper.Program.s_filledAutoEventForTest.WaitOne();

            // 数据库里应该有这笔订单！
            using (var db = new ExchangeDb())
            {
                var count = db.Orders.Count();
                Assert.AreEqual(orderCapacity + 2, count);
                var matched = db.Orders.Count(o => o.VolumeRemaining == 0);
                Assert.AreEqual(orderCapacity / 2 + 1, matched);
            }

            // 买单现在已经完全匹配，再下足够多的卖单以便填满内存容量
            for (var i = 0; i < orderCapacity / 2; ++i)
            {
                controller._uid = TestConstants.DEFAULT_ASK_USER_ID;
                await controller.SellLimit(market, askprice, 1);
            }
            // 延时5秒
            await Task.Delay(5000);
            Assert.AreEqual(orderCapacity, OrderMatchHandler.s_MemOrderBookRepo[0].Capacity);
            Assert.AreEqual(orderCapacity, OrderMatchHandler.s_MemOrderBookRepo[0].Count);

            // 再下一个卖单
            controller._uid = TestConstants.DEFAULT_ASK_USER_ID;
            var aid = await controller.SellLimit(market, askprice, 1);
            await Task.Delay(1000);
            Assert.AreEqual(orderCapacity, OrderMatchHandler.s_MemOrderBookRepo[0].Capacity);
            Assert.AreEqual(orderCapacity, OrderMatchHandler.s_MemOrderBookRepo[0].Count);
            var oid = Guid.Parse(aid.Result);

            // 数据库里应该有这笔订单！
            using (var db = new ExchangeDb())
            {
                var count = db.Orders.Count();
                Assert.AreEqual(orderCapacity + 2 + orderCapacity / 2 + 1, count);
                Assert.IsTrue(db.Orders.Any(o => o.Id == oid));
            }

            // 下一个大买单，消化一半的卖单
            controller._uid = TestConstants.DEFAULT_ASK_USER_ID;
            var bid = await controller.BuyLimit(market, askprice + 1, orderCapacity / 2);
            // 延时5秒
            await Task.Delay(5000);
            Assert.AreEqual(orderCapacity, OrderMatchHandler.s_MemOrderBookRepo[0].Capacity);
            // 内存里的订单消化一半
            Assert.AreEqual(orderCapacity / 2, OrderMatchHandler.s_MemOrderBookRepo[0].Count);
            // 数据库里应该有这笔订单！
            oid = Guid.Parse(bid.Result);
            using (var db = new ExchangeDb())
            {
                Assert.IsTrue(db.Orders.Any(o => o.Id == oid));
            }

            // 再下一个大买单，消化所有的卖单，要求数据库里的订单也消化掉
            controller._uid = TestConstants.DEFAULT_ASK_USER_ID;
            bid = await controller.BuyLimit(market, askprice + 1, orderCapacity / 2 + 1);
            // 延时5秒
            await Task.Delay(5000);
            Assert.AreEqual(orderCapacity, OrderMatchHandler.s_MemOrderBookRepo[0].Capacity);
            // 内存里的订单全部消化
            Assert.AreEqual(0, OrderMatchHandler.s_MemOrderBookRepo[0].Count);
            // 数据库里应该有这笔订单！
            oid = Guid.Parse(bid.Result);
            using (var db = new ExchangeDb())
            {
                Assert.IsTrue(db.Orders.Any(o => o.Id == oid));
                // 订单全部消化
                var notMatched = db.Orders.Count(o => o.VolumeRemaining > 0);
                Assert.AreEqual(0, notMatched);
            }
        }
    }
#endif
}
